package com.cysoft.starter.log.core.configuration;

import com.cysoft.starter.log.config.LogAutoConfiguration;
import com.cysoft.starter.log.core.aspect.LogAspect;
import com.cysoft.starter.log.core.exception.LogException;
import com.cysoft.starter.log.core.helper.DataBaseHelper;
import com.cysoft.starter.log.core.properties.OperateLogProperties;
import com.cysoft.starter.log.core.properties.output.JdbcProperties;
import com.cysoft.starter.log.core.repository.impl.JdbcRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.Objects;

/**
 * @author 玖零
 * @version v0.0.1
 * @project cysoft-spring-boot-starter-log
 * @package com.cysoft.starter.log.core.configuration
 * @date 2025-02-04 00:43
 * @desc jdbc日志切面配置类
 */
@Slf4j
@Configuration
@ConditionalOnBean(name = "jdbcConfiguration")
@AutoConfigureAfter(LogAutoConfiguration.class)
public class JdbcConfiguration {

    /**
     * 数据源配置信息
     */
    private final DataSourceProperties dataSourceProperties;

    /**
     * 操作日志配置信息
     */
    private final OperateLogProperties operateLogProperties;

    /**
     * jdbc日志切面配置 构造方法
     * @param operateLogProperties
     */
    public JdbcConfiguration(OperateLogProperties operateLogProperties,DataSourceProperties dataSourceProperties) {
        log.info("初始化JdbcConfiguration，注入操作日志配置信息和数据源配置信息");
        this.operateLogProperties = operateLogProperties;
        this.dataSourceProperties = dataSourceProperties;
    }

    /**
     * 注册jdbc日志切面
     * @return
     */
    @Bean
    public LogAspect jdbcLogAspect(){
        log.info("******************开始初始化jdbcLogAspect******************");
        //构建jdbcTemplate对象
        JdbcTemplate jdbcTemplate = this.buildJdbcTemplate();
        log.info("成功构建JdbcTemplate对象");
        //获取jdbc配置信息
        JdbcProperties jdbcProperties = operateLogProperties.getOutput().getJdbc();
        log.info("成功获取Jdbc配置信息");
        //校验jdbc配置信息
        this.checkConfig(jdbcTemplate,jdbcProperties);
        log.info("Jdbc配置信息校验通过");
        //构建jdbc日志切面
        log.info("开始构建Jdbc日志切面");
        LogAspect logAspect = new LogAspect(new JdbcRepository(jdbcTemplate, jdbcProperties), operateLogProperties);
        log.info("Jdbc日志切面构建成功，日志切面对象: {}", logAspect);
        return logAspect;
    }

    /**
     * 构建jdbcTemplate对象
     * @return
     */
    private JdbcTemplate buildJdbcTemplate() {
        log.info("开始构建JdbcTemplate");
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProperties.initializeDataSourceBuilder().build());
        log.info("JdbcTemplate构建完成");
        return jdbcTemplate;
    }

    /**
     * 配置校验
     *
     * @param jdbcTemplate   jdbcTemplate
     * @param jdbcProperties jdbc配置信息
     */
    private void checkConfig(JdbcTemplate jdbcTemplate, JdbcProperties jdbcProperties) {
        log.info("开始进行Jdbc配置信息校验");
        // 校验jdbc配置信息
        checkJdbcProperties(jdbcProperties);
        log.info("Jdbc配置信息语法校验通过");
        // 校验数据库连接是否有效
        if (!isDatabaseConnectionValid(jdbcTemplate, jdbcProperties)) {
            throw new LogException("数据库连接异常");
        }
        log.info("数据库连接校验通过");
        // 校验表是否存在
        verifyOrCreateTable(jdbcTemplate, jdbcProperties);
        log.info("表校验或创建操作完成");
    }


    /**
     * 校验jdbc配置信息
     * @param jdbcProperties
     */
    private void checkJdbcProperties(JdbcProperties jdbcProperties) {
        log.info("开始校验创建表的ddl语句");
        //判断创建表的ddl语句是否为空,如果不为空校验准确性
        validateSql(jdbcProperties.getCreateTableDdl(), "create table ddl语句不正确，请检查配置信息！");
        log.info("创建表的ddl语句校验通过");
        log.info("开始校验插入数据的sql语句");
        //判断插入数据的sql语句是否为空,如果不为空校验准确性
        validateSql(jdbcProperties.getInsertData(), "insert data sql语句不正确，请检查配置信息！");
        log.info("插入数据的sql语句校验通过");
    }

    /**
     * 验证SQL语句是否包含占位符
     * @param sql              被校验的sql语句
     * @param errorMessage     错误提示
     */
    private void validateSql(String sql, String errorMessage) {
        log.info("开始验证SQL语句是否包含占位符，SQL语句: {}", sql);
        if (Objects.nonNull(sql) && !sql.contains("%s")) {
            log.error("SQL语句校验失败: {}", errorMessage);
            throw new LogException(errorMessage);
        }
        log.info("SQL语句包含占位符，校验通过");
    }


    /**
     * 校验数据库连接是否有效
     *
     * @param jdbcTemplate   jdbcTemplate
     * @param jdbcProperties jdbc配置信息
     * @return 数据库连接是否有效
     */
    private boolean isDatabaseConnectionValid(JdbcTemplate jdbcTemplate,JdbcProperties jdbcProperties) {
        log.info("开始校验数据库连接是否有效");
        String checkSql = getCheckDatabaseConnectionStatusSql(jdbcProperties);
        log.info("获取到数据库连接校验SQL语句: {}", checkSql);
        try {
            Integer result = jdbcTemplate.queryForObject(checkSql, Integer.class);
            boolean isValid = result != null && result == 1;
            log.info("数据库连接校验结果: {}", isValid ? "有效" : "无效");
            return isValid;
        } catch (Exception e) {
            log.error("数据库连接校验失败", e);
            return false;
        }
    }


    /**
     * 获取数据库连接校验SQL语句
     * @param jdbcProperties jdbc配置信息
     * @return 数据库连接校验SQL语句
     */
    private String getCheckDatabaseConnectionStatusSql(JdbcProperties jdbcProperties) {
        log.info("开始获取数据库连接校验SQL语句");
        return Objects.nonNull(jdbcProperties.getCheckDatabaseConnectionStatus())
                ? jdbcProperties.getCheckDatabaseConnectionStatus()
                : jdbcProperties.getDataSourceEnum().getJdbcBase().getCheckDatabaseConnectionStatus();
    }


    /**
     * 校验表是否存在，不存在则创建表
     * @param jdbcTemplate     jdbcTemplate
     * @param jdbcProperties   jdbc配置信息
     */
    private void verifyOrCreateTable(JdbcTemplate jdbcTemplate, JdbcProperties jdbcProperties) {
        log.info("开始校验表是否存在，表名: {}", jdbcProperties.getTableName());
        String checkTableSql = getCheckTableExistsSql(jdbcProperties);
        log.info("获取到检查表是否存在的SQL语句: {}", checkTableSql);
        try {
            Integer result = jdbcTemplate.queryForObject(checkTableSql, Integer.class);
            if (result == null || result != 1) {
                String createTableSql = getCreateTableSql(jdbcProperties);
                log.info("表 {} 不存在，开始创建表，创建表的SQL语句: {}", jdbcProperties.getTableName(), createTableSql);
                jdbcTemplate.execute(createTableSql);
                log.info("表 {} 已创建", jdbcProperties.getTableName());
            }else {
                log.info("表 {} 已存在", jdbcProperties.getTableName());
            }
        } catch (Exception e) {
            log.error("校验或创建表时出现异常，表名: {}", jdbcProperties.getTableName(), e);
            throw new LogException("校验或创建表时出现异常", e);
        }
    }


    /**
     * 获取检查表是否存在的 SQL 语句
     * @param jdbcProperties jdbc 配置信息
     * @return 检查表是否存在的 SQL 语句
     */
    private String getCheckTableExistsSql(JdbcProperties jdbcProperties) {
        log.info("开始获取检查表是否存在的SQL语句");
        String databaseName = findDatabaseName(jdbcProperties.getDatabaseName());
        log.info("获取到数据库名称: {}", databaseName);
        String tableName = jdbcProperties.getTableName();
        return jdbcProperties.getCheckTableExists() != null
                ? String.format(jdbcProperties.getCheckTableExists(), tableName, databaseName)
                : String.format(jdbcProperties.getDataSourceEnum().getJdbcBase().getCheckTableExists(), tableName, databaseName);
    }


    /**
     * 获取有效的数据库名称
     * @param providedDatabaseName 传入的数据库名称，可能为 null
     * @return 有效的数据库名称
     */
    private String findDatabaseName(String providedDatabaseName) {
        log.info("开始获取有效的数据库名称");
        if (Objects.isNull(providedDatabaseName)) {
            try {
                return DataBaseHelper.extractDatabaseName(dataSourceProperties.getUrl());
            } catch (Exception e) {
                log.error("从数据源 URL 中提取数据库名称时出现异常，URL: {}", dataSourceProperties.getUrl(), e);
                throw new LogException("无法从数据源 URL 中提取数据库名称，请检查数据源配置", e);
            }
        }
        return providedDatabaseName;
    }


    /**
     * 获取创建表的SQL语句
     *
     * @param jdbcProperties jdbc配置信息
     * @return 创建表的SQL语句
     */
    private String getCreateTableSql(JdbcProperties jdbcProperties) {
        log.info("开始获取创建表的SQL语句");
        return Objects.nonNull(jdbcProperties.getCreateTableDdl())
                ? String.format(jdbcProperties.getCreateTableDdl(), jdbcProperties.getTableName())
                : String.format(jdbcProperties.getDataSourceEnum().getJdbcBase().getCreateTableDdl(), jdbcProperties.getTableName());
    }

}
